﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace ICSharpCode.CodeConverter.Util.FromRoslyn;

internal partial class SymbolEquivalenceComparer
{
    internal class ParameterSymbolEqualityComparer : IEqualityComparer<IParameterSymbol>
    {
        private readonly SymbolEquivalenceComparer _symbolEqualityComparer;
        private readonly bool _distinguishRefFromOut;

        public ParameterSymbolEqualityComparer(
            SymbolEquivalenceComparer symbolEqualityComparer,
            bool distinguishRefFromOut)
        {
            _symbolEqualityComparer = symbolEqualityComparer;
            _distinguishRefFromOut = distinguishRefFromOut;
        }

        public bool Equals(
            IParameterSymbol x,
            IParameterSymbol y,
            Dictionary<INamedTypeSymbol, INamedTypeSymbol> equivalentTypesWithDifferingAssemblies,
            bool compareParameterName,
            bool isCaseSensitive)
        {
            if (ReferenceEquals(x, y))
            {
                return true;
            }

            if (x == null || y == null)
            {
                return false;
            }

            var nameComparisonCheck = true;
            if (compareParameterName)
            {
                nameComparisonCheck = isCaseSensitive ?
                    x.Name == y.Name
                    : string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
            }

            // See the comment in the outer type.  If we're comparing two parameters for
            // equality, then we want to consider method type parameters by index only.

            return
                AreRefKindsEquivalent(x.RefKind, y.RefKind, _distinguishRefFromOut) &&
                nameComparisonCheck &&
                _symbolEqualityComparer.GetEquivalenceVisitor().AreEquivalent(x.CustomModifiers, y.CustomModifiers, equivalentTypesWithDifferingAssemblies) &&
                _symbolEqualityComparer.SignatureTypeEquivalenceComparer.Equals(x.Type, y.Type, equivalentTypesWithDifferingAssemblies);
        }

        public bool Equals(IParameterSymbol x, IParameterSymbol y)
        {
            return this.Equals(x, y, null, false, false);
        }

        public bool Equals(IParameterSymbol x, IParameterSymbol y, bool compareParameterName, bool isCaseSensitive)
        {
            return this.Equals(x, y, null, compareParameterName, isCaseSensitive);
        }

        public int GetHashCode(IParameterSymbol x)
        {
            if (x == null)
            {
                return 0;
            }

            return
                Hash.Combine(x.IsRefOrOut(),
                    _symbolEqualityComparer.SignatureTypeEquivalenceComparer.GetHashCode(x.Type));
        }
    }

    public static bool AreRefKindsEquivalent(RefKind rk1, RefKind rk2, bool distinguishRefFromOut)
    {
        return distinguishRefFromOut
            ? rk1 == rk2
            : (rk1 == RefKind.None) == (rk2 == RefKind.None);
    }
}